﻿using AuSys.Services.App.ConfigReader;
using AuSys.Services.App.UserInfo.Abstractions;
using AuSys.Utils.Reflection;
using Microsoft.AspNetCore.Mvc.Filters;
using RqEx = AuSys.Utils.Exceptions.RequestInvalidException;

namespace AuSys.Services.App.ActionFilters
{
    /// <summary>
    /// 根据controller或action的UserAccessScope标注<br/>
    /// 判断当前用户是否可以直接允许/拒绝<br/>
    /// <b>必须在UserAccessFilter前执行</b>
    /// </summary>
    /// <param name="configuration"></param>
    public class UserAccessScopeFilter(
        IUserInfoService userInfoService,
        UserAccessScopeConfigReader configReader
        ) : IAsyncActionFilter
    {
        public const string userAccessScopeCheckedItemKey = "userAccessScopeChecked";
        public Task OnActionExecutionAsync(
            ActionExecutingContext context, ActionExecutionDelegate next)
        {
            //如果无相关设置，直接跳过
            var cfg = configReader.Get();
            if (cfg.NoNeed)
                return next();
            //是管理员，直接跳过
            var myInfo = userInfoService.GetUserInfo();
            if(myInfo.IsAdmin)
                return next();

            int myId = myInfo.Id;
            //获取当前操作的scope集合
            var scopesHere = ApiActionReader.GetUserAccessScope(
                context.Controller, context.HttpContext);
            if (scopesHere.Contains(UserAccessScopeSkipCheck.name))
            {
                //当前资源无需进行任何检查
                return next();
            }
            //scope名统一用小写判断（获取和比较时都ToLower）
            var perScopeCfgsHere = cfg.PerScope
                .FindAll(x => scopesHere.Any(x.MatchName));
            var aboutMe = perScopeCfgsHere
                .FindAll(x => x.ContainUser(myId));
            if (aboutMe.Count > 0)
            {
                if (aboutMe.Last().Flag == true)
                {
                    //关于当前用户最后一个配置设为了允许
                    //在HttpContext里设置“已检查过”标记
                    context.HttpContext.Items.Add(
                        userAccessScopeCheckedItemKey, true);
                }
                else
                {
                    //抛出RqEx拒绝访问
                    throw new RqEx("无权限，请咨询管理员");
                }
            }
            else
            {
                //当前Scopes无对该用户的配置，但默认不允许该用户进行任何操作
                //抛出RqEx拒绝访问
                if(cfg.DefaultRejectUserIds.Contains(myId))
                    throw new RqEx("无权限，请咨询管理员");
            }
            return next();
        }
    }

    /// <summary>
    /// 为controller或action标注其所属的UserAccessScope名称<br/>
    /// action中的若设为Reverse=true则可以忽略controller中的设置
    /// </summary>
    /// <param name="name">scope名称</param>
    [AttributeUsage(
        AttributeTargets.Class | AttributeTargets.Method,
        AllowMultiple = true)]
    public class UserAccessScopeAttribute(string name) : Attribute
    {
        public string Name { get; set; } = name;
        public bool Reverse { get; set; } = false;
    }
    /// <summary>
    /// 表示当前controller或action无需判断<br/>
    /// 任何用户都可以访问（包括被设为DefaultRejectUserIds的）<br/>
    /// 优先级高于其他UserAccessScope标注
    /// </summary>
    public class UserAccessScopeSkipCheck() : UserAccessScopeAttribute(name)
    {
        //得是全小写
        public const string name = "__skipcheck";
    }
}
